4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
16 namespace Microsoft
.JScript
{
19 using System
.Reflection
;
20 using System
.Reflection
.Emit
;
21 using System
.Diagnostics
;
23 public sealed class NumericUnary
: UnaryOp
{
24 private Object metaData
= null;
25 private JSToken operatorTok
;
26 private MethodInfo operatorMeth
;
29 internal NumericUnary(Context context
, AST operand
, JSToken operatorTok
)
30 : base(context
, operand
){
31 this.operatorTok
= operatorTok
;
32 this.operatorMeth
= null;
36 public NumericUnary(int operatorTok
)
37 : this(null, null, (JSToken
)operatorTok
){
40 internal override Object
Evaluate(){
41 return this.EvaluateUnary(this.operand
.Evaluate());
45 [DebuggerStepThroughAttribute
]
46 [DebuggerHiddenAttribute
]
48 public Object
EvaluateUnary(Object v
){
49 IConvertible ic
= Convert
.GetIConvertible(v
);
50 switch(Convert
.GetTypeCode(v
, ic
)){
51 case TypeCode
.Empty
: return this.EvaluateUnary(Double
.NaN
);
52 case TypeCode
.DBNull
: return this.EvaluateUnary(0);
53 case TypeCode
.Boolean
: return this.EvaluateUnary(ic
.ToBoolean(null) ? 1 : 0);
54 case TypeCode
.Char
: return this.EvaluateUnary((int)ic
.ToChar(null));
61 int i
= ic
.ToInt32(null);
62 switch (this.operatorTok
){
63 case JSToken
.BitwiseNot
:
65 case JSToken
.LogicalNot
:
68 if (i
== 0) return -(double)i
;
69 if (i
== Int32
.MinValue
) return (ulong)-(double)i
;
74 throw new JScriptException(JSError
.InternalError
, this.context
);
78 uint ui
= ic
.ToUInt32(null);
79 switch (this.operatorTok
){
80 case JSToken
.BitwiseNot
:
82 case JSToken
.LogicalNot
:
85 if (ui
!= 0 && ui
<= Int32
.MaxValue
)
92 throw new JScriptException(JSError
.InternalError
, this.context
);
96 long l
= ic
.ToInt64(null);
97 switch (this.operatorTok
){
98 case JSToken
.BitwiseNot
:
100 case JSToken
.LogicalNot
:
103 if (l
== 0 || l
== Int64
.MinValue
) return -(double)l
;
108 throw new JScriptException(JSError
.InternalError
, this.context
);
111 case TypeCode
.UInt64
:
112 ulong ul
= ic
.ToUInt64(null);
113 switch (this.operatorTok
){
114 case JSToken
.BitwiseNot
:
116 case JSToken
.LogicalNot
:
119 if (ul
!= 0 && ul
<= Int64
.MaxValue
)
126 throw new JScriptException(JSError
.InternalError
, this.context
);
129 case TypeCode
.Single
:
130 case TypeCode
.Double
:
131 double d
= ic
.ToDouble(null);
132 switch (this.operatorTok
){
133 case JSToken
.BitwiseNot
:
134 return ~
(int)Runtime
.DoubleToInt64(d
);
135 case JSToken
.LogicalNot
:
136 return !Convert
.ToBoolean(d
);
142 throw new JScriptException(JSError
.InternalError
, this.context
);
145 case TypeCode
.String
:
146 goto no_overload_case
;
149 MethodInfo oper
= this.GetOperator(v
.GetType());
151 return oper
.Invoke(null, (BindingFlags
)0, JSBinder
.ob
, new Object
[]{v}
, null);
153 switch (this.operatorTok
){
154 case JSToken
.BitwiseNot
:
155 return ~Convert
.ToInt32(v
, ic
);
156 case JSToken
.LogicalNot
:
157 return !Convert
.ToBoolean(v
, ic
);
159 return -Convert
.ToNumber(v
, ic
);
161 return Convert
.ToNumber(v
, ic
);
163 throw new JScriptException(JSError
.InternalError
, this.context
);
167 private MethodInfo
GetOperator(IReflect ir
){
168 Type t
= ir
is Type
? (Type
)ir
: Typeob
.Object
;
170 return this.operatorMeth
;
172 if (Convert
.IsPrimitiveNumericType(t
) || Typeob
.JSObject
.IsAssignableFrom(t
)){
173 this.operatorMeth
= null;
176 switch (this.operatorTok
){
177 case JSToken
.BitwiseNot
:
178 this.operatorMeth
= t
.GetMethod("op_OnesComplement", BindingFlags
.Public
|BindingFlags
.Static
, JSBinder
.ob
, new Type
[]{t}
, null); break;
179 case JSToken
.LogicalNot
:
180 this.operatorMeth
= t
.GetMethod("op_LogicalNot", BindingFlags
.Public
|BindingFlags
.Static
, JSBinder
.ob
, new Type
[]{t}
, null); break;
182 this.operatorMeth
= t
.GetMethod("op_UnaryNegation", BindingFlags
.Public
|BindingFlags
.Static
, JSBinder
.ob
, new Type
[]{t}
, null); break;
184 this.operatorMeth
= t
.GetMethod("op_UnaryPlus", BindingFlags
.Public
|BindingFlags
.Static
, JSBinder
.ob
, new Type
[]{t}
, null); break;
186 throw new JScriptException(JSError
.InternalError
, this.context
);
188 if (this.operatorMeth
== null ||
189 (operatorMeth
.Attributes
& MethodAttributes
.SpecialName
) == 0 ||
190 operatorMeth
.GetParameters().Length
!= 1)
191 this.operatorMeth
= null;
192 if (this.operatorMeth
!= null)
193 this.operatorMeth
= new JSMethodInfo(this.operatorMeth
);
194 return this.operatorMeth
;
197 internal override IReflect
InferType(JSField inference_target
){
198 Debug
.Assert(Globals
.TypeRefs
.InReferenceContext(this.type
));
200 if (this.type
== null || inference_target
!= null){
201 oper
= this.GetOperator(this.operand
.InferType(inference_target
));
203 oper
= this.GetOperator(this.type
);
205 this.metaData
= oper
;
206 return oper
.ReturnType
;
208 if (this.operatorTok
== JSToken
.LogicalNot
) return Typeob
.Boolean
;
209 switch (Type
.GetTypeCode(this.type
)){
210 case TypeCode
.Empty
: return this.operatorTok
== JSToken
.BitwiseNot
? Typeob
.Int32
: Typeob
.Double
;
211 case TypeCode
.Object
: return Typeob
.Object
;
212 case TypeCode
.DBNull
: return Typeob
.Int32
;
213 case TypeCode
.Boolean
: return Typeob
.Int32
;
214 case TypeCode
.SByte
: return (this.operatorTok
== JSToken
.BitwiseNot
) ? Typeob
.SByte
: Typeob
.Int32
;
215 case TypeCode
.Char
: return Typeob
.Int32
;
216 case TypeCode
.Byte
: return (this.operatorTok
== JSToken
.BitwiseNot
) ? Typeob
.Byte
: Typeob
.Int32
;
217 case TypeCode
.Int16
: return (this.operatorTok
== JSToken
.BitwiseNot
) ? Typeob
.Int16
: Typeob
.Int32
;
218 case TypeCode
.UInt16
: return (this.operatorTok
== JSToken
.BitwiseNot
) ? Typeob
.UInt16
: Typeob
.Int32
;
219 case TypeCode
.Int32
: return Typeob
.Int32
;
220 case TypeCode
.UInt32
: return this.operatorTok
== JSToken
.Minus
? Typeob
.Double
: Typeob
.UInt32
;
221 case TypeCode
.Int64
: return Typeob
.Int64
;
222 case TypeCode
.UInt64
: return this.operatorTok
== JSToken
.Minus
? Typeob
.Double
: Typeob
.UInt64
;
223 case TypeCode
.Single
:
224 case TypeCode
.Double
:
225 case TypeCode
.String
: return this.operatorTok
== JSToken
.BitwiseNot
? Typeob
.Int32
: Typeob
.Double
;
227 if (Typeob
.JSObject
.IsAssignableFrom(this.type
))
228 return Typeob
.Double
;
230 return Typeob
.Object
;
233 internal override void TranslateToConditionalBranch(ILGenerator il
, bool branchIfTrue
, Label label
, bool shortForm
){
234 if (this.operatorTok
== JSToken
.LogicalNot
)
235 this.operand
.TranslateToConditionalBranch(il
, !branchIfTrue
, label
, shortForm
);
237 base.TranslateToConditionalBranch(il
, branchIfTrue
, label
, shortForm
);
240 internal override void TranslateToIL(ILGenerator il
, Type rtype
){
241 if (this.metaData
== null){
242 Type rt
= this.operatorTok
== JSToken
.LogicalNot
? Typeob
.Boolean
: Typeob
.Double
;
243 if (Convert
.IsPrimitiveNumericType(rtype
) && Convert
.IsPromotableTo(this.type
, rtype
))
245 if (this.operatorTok
== JSToken
.BitwiseNot
&& !Convert
.IsPrimitiveIntegerType(rt
)){
247 if (!Convert
.IsPrimitiveIntegerType(rt
))
250 this.operand
.TranslateToIL(il
, this.type
);
251 Convert
.Emit(this, il
, this.type
, rt
, true);
252 switch (this.operatorTok
){
253 case JSToken
.BitwiseNot
:
254 il
.Emit(OpCodes
.Not
);
256 case JSToken
.LogicalNot
:
257 Convert
.Emit(this, il
, rt
, Typeob
.Boolean
, true);
259 il
.Emit(OpCodes
.Ldc_I4_0
);
260 il
.Emit(OpCodes
.Ceq
);
263 il
.Emit(OpCodes
.Neg
);
268 throw new JScriptException(JSError
.InternalError
, this.context
);
270 Convert
.Emit(this, il
, rt
, rtype
);
273 if (this.metaData
is MethodInfo
){
274 MethodInfo oper
= (MethodInfo
)this.metaData
;
275 ParameterInfo
[] pars
= oper
.GetParameters();
276 this.operand
.TranslateToIL(il
, pars
[0].ParameterType
);
277 il
.Emit(OpCodes
.Call
, oper
);
278 Convert
.Emit(this, il
, oper
.ReturnType
, rtype
);
281 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
282 //Compile operands to objects and devolve the decision making to run time thunks
283 il
.Emit(OpCodes
.Ldloc
, (LocalBuilder
)this.metaData
);
284 this.operand
.TranslateToIL(il
, Typeob
.Object
);
285 il
.Emit(OpCodes
.Call
, CompilerGlobals
.evaluateUnaryMethod
);
286 Convert
.Emit(this, il
, Typeob
.Object
, rtype
);
289 internal override void TranslateToILInitializer(ILGenerator il
){
290 IReflect rtype
= this.InferType(null);
291 this.operand
.TranslateToILInitializer(il
);
292 if (rtype
!= Typeob
.Object
)
294 this.metaData
= il
.DeclareLocal(Typeob
.NumericUnary
);
295 ConstantWrapper
.TranslateToILInt(il
, (int)this.operatorTok
);
296 il
.Emit(OpCodes
.Newobj
, CompilerGlobals
.numericUnaryConstructor
);
297 il
.Emit(OpCodes
.Stloc
, (LocalBuilder
)this.metaData
);